﻿using CPAIS.Adapter;
using CPAIS.Model;
using CPAIS.Updater;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.SessionState;

namespace CPAIS.Web.AjaxHandlers
{
    public class LDAAnalysisHandler : AjaxHandler
    {
        public LDAAnalysisHandler(HttpContext context) : base(context) { }
        
        /// <summary>
        /// AJAX request:
        ///     LDAAnalysis
        /// 
        /// Request Parameters:
        ///     logName
        ///     K
        ///     alpha
        ///     beta
        ///     iterations
        ///     
        /// Response:
        ///     None
        ///     
        /// </summary>
        #region Ajax Request LDAAnalysis
        
        public void LDAAnalysis()
        {
            string logName = Params[Constants.LogName];
            MLManager mlManager = Session[Constants.MLManager] as MLManager;
            LDAGibbsAdapter adapter = mlManager.GetLDAAdapter(logName);
            AnalysisUpdater updater = new AnalysisUpdater(Session, adapter,
                int.Parse(Params[Constants.K]),
                double.Parse(Params[Constants.Alpha]),
                double.Parse(Params[Constants.Beta]),
                int.Parse(Params[Constants.Iterations]));
            Session.Add(Constants.LDAAnalysisUpdater, updater);
            ThreadPool.QueueUserWorkItem(DoAnalysising, updater);
        }

        private class AnalysisUpdater : ProgressUpdater
        {
            public HttpSessionState Session { get; set; }
            public LDAGibbsAdapter Adapter { get; set; }
            public int K { get; set; }
            public double Alpha { get; set; }
            public double Beta { get; set; }
            public int Iterations { get; set; }

            public AnalysisUpdater(HttpSessionState session, LDAGibbsAdapter adapter, int K, double alpha, double beta, int iterations)
            {
                this.Session = session;
                this.Adapter = adapter;
                this.K = K;
                this.Alpha = alpha;
                this.Beta = beta;
                this.Iterations = iterations;
            }

            protected override bool OnUpdateMessage(string message)
            {
                if (Session[Constants.LDAAnalysisUpdater] != null)
                {
                    return true;
                }
                return false;
            }

            protected override bool OnUpdateProgress(double progress)
            {
                if (Session[Constants.LDAAnalysisUpdater] != null)
                {
                    return true;
                }
                return false;
            }
        }

        private void DoAnalysising(object state)
        {
            AnalysisUpdater updater = state as AnalysisUpdater;
            LDAGibbsAdapter adapter = updater.Adapter;
            adapter.Execute(updater.K, updater.Alpha, updater.Beta, updater.Iterations, updater);
        }

        #endregion

        /// <summary>
        /// AJAX request:
        ///     CancelLDAAnalysis
        /// 
        /// Request Parameters:
        ///     logName
        /// 
        /// Response:
        ///     None
        /// </summary>
        #region Ajax Request CancelLDAAnalysis
        
        public void CancelLDAAnalysis()
        {
            Session.Remove(Constants.LDAAnalysisUpdater);
        }

        #endregion

        /// <summary>
        /// AJAX request:   
        ///     QueryLDAAnalysisProgress
        /// 
        /// Request Parameters:
        ///     logName
        /// 
        /// Response: 
        ///     QueryLDAAnalysisProgressPackage
        /// </summary>
        #region Ajax Request QueryLDAAnalysisProgress
                
        public void QueryLDAAnalysisProgress()
        {
            AnalysisUpdater updater = Session[Constants.LDAAnalysisUpdater] as AnalysisUpdater;
            QueryLDAAnalysisProgressPackage package = new QueryLDAAnalysisProgressPackage
            {
                state = updater.State.ToString(),
                msg = updater.Message,
                progress = updater.Progress
            };
            switch (updater.State)
            {
                case UpdateState.Failed:
                case UpdateState.Completed:
                    Session.Remove(Constants.LDAAnalysisUpdater);
                    break;
            }
            WriteJson(package);
        }

        private class QueryLDAAnalysisProgressPackage
        {
            public string state { get; set; }
            public string msg { get; set; }
            public double progress { get; set; }
        }

        #endregion

        /// <summary>
        /// AJAX request:
        ///     GetLDAAnalysisSummary
        /// 
        /// Request Parameters:
        ///     logName
        /// 
        /// Response: 
        ///     GetLDAAnalysisSummaryPackage
        /// </summary>
        #region Ajax Request GetLDAAnalysisSummary

        public void GetLDAAnalysisSummary()
        {
            string logName = Params[Constants.LogName];
            MLManager mlManager = Session[Constants.MLManager] as MLManager;
            LogModel logModel = mlManager.GetLogModel(logName);
            GetLDAAnalysisSummaryPackage package = new GetLDAAnalysisSummaryPackage();
            if (logModel == null)
            {
                package.state = false;
                package.data = null;
            }
            else
            {
                LDAGibbsAdapter adapter = mlManager.GetLDAAdapter(logName);
                package.state = true;
                package.data = new object[]
                {
                    new object[]{"Alpha", adapter.Alpha},
                    new object[]{"Beta", adapter.Beta},
                    new object[]{"Iterations", adapter.Iterations},
                    new object[]{"Document Size", adapter.DocumentSize},
                    new object[]{"Vocabulary Size", adapter.VocabularySize},
                    new object[]{"Topic Size", adapter.TopicSize},
                    new object[]{"Perplexity", adapter.Perplexity.ToString("0.###")},
                    new object[]{"KL Divergence", adapter.KLDivergence.ToString("0.###")},
                    new object[]{"Entropy", adapter.Entropy.ToString("0.###")}
                };
            }
            WriteJson(package);
        }

        private class GetLDAAnalysisSummaryPackage
        {
            public bool state { get; set; }
            public object[] data { get; set; }
        }

        #endregion

        /// <summary>
        /// AJAX request:
        ///     GetLDADocData
        /// 
        /// Request Parameters:
        ///     logName
        /// 
        /// Response: 
        ///     TraceDataPackage
        /// </summary>
        #region Ajax Request GetLDADocData

        public void GetLDADocData()
        {
            string path = Params[Constants.LogName];
            int start = int.Parse(Params[Constants.Start]);
            int limit = int.Parse(Params[Constants.Limit]);

            MLManager mlManager = Session[Constants.MLManager] as MLManager;
            LogModel logModel = mlManager.GetLogModel(path);
            if (logModel != null)
            {
                List<TraceData> traceData = new List<TraceData>();
                foreach (var trace in logModel.Traces)
                {
                    TraceData data = new TraceData
                    {
                        pid = trace.ID
                    };
                    traceData.Add(data);
                }

                TraceDataPackage package = new TraceDataPackage();
                int count = traceData.Count;
                int left = count - start;
                package.total = count;
                package.items = traceData.GetRange(start, limit <= left ? limit : left);
                WriteJson(package);
            }
        }

        private class TraceData
        {
            public string pid { get; set; }
        }

        private class TraceDataPackage
        {
            public int total { get; set; }
            public List<TraceData> items { get; set; }
        }

        #endregion

        /// <summary>
        /// AJAX request:
        ///     GetLDADocTopicProbability
        /// 
        /// Request Parameters:
        ///     logName
        ///     pid
        /// 
        /// Response: 
        ///     DocTopicProbabilityPackage
        /// </summary>
        #region Ajax Request GetLDADocTopicProbability

        public void GetLDADocTopicProbability()
        {
            string path = Params[Constants.LogName];
            string pid = Params[Constants.PID];
            MLManager mlManager = Session[Constants.MLManager] as MLManager;
            LDAGibbsAdapter adapter = mlManager.GetLDAAdapter(path);
            if (adapter != null && adapter.DocTopicAssoc.ContainsKey(pid))
            {
                double[] probs = adapter.DocTopicAssoc[pid];
                int length = probs.Length;
                DocTopicProbabilityPackage[] package = new DocTopicProbabilityPackage[length];
                for (int i = 0; i < length; i++)
                {
                    package[i] = new DocTopicProbabilityPackage();
                    package[i].topicNo = "CP Pattern " + (i + 1).ToString();
                    package[i].probability = probs[i];
                }
                WriteJson(package);
            }            
        }

        private class DocTopicProbabilityPackage
        {
            public string topicNo { get; set; }
            public double probability { get; set; }
        }

        #endregion

        /// <summary>
        /// AJAX request:
        ///     GetLDATopicData
        /// 
        /// Request Parameters:
        ///     logName
        /// 
        /// Response: 
        ///     TopicDataPackage
        /// </summary>
        #region Ajax Request GetLDATopicData

        public void GetLDATopicData()
        {
            string path = Params[Constants.LogName];
            
            MLManager mlManager = Session[Constants.MLManager] as MLManager;
            LDAGibbsAdapter adapter = mlManager.GetLDAAdapter(path);
            TopicDataPackage package = new TopicDataPackage { count = 0, items = null };
            if (adapter != null)
            {
                var topics = adapter.TopicWordAssoc;
                package.count = topics.Length;
                package.items = new List<TopicData>();
                foreach (var topic in topics)
                {
                    TopicData topicData = new TopicData
                    {
                        prob = topic.Prob,
                        actions = new List<ActionData>()
                    };
                    foreach (var action in topic.Actions)
                    {
                        if (action.ActionProb >= 0.001)
                        {
                            topicData.actions.Add(new ActionData
                            {
                                name = action.ActionType,
                                prob = action.ActionProb
                            });
                        }
                    }
                    package.items.Add(topicData);
                }
            }
            WriteJson(package);
        }

        private class ActionData
        {
            public string name { get; set; }
            public double prob { get; set; }
        }

        private class TopicData
        {
            public double prob;
            public List<ActionData> actions;
        }

        private class TopicDataPackage
        {
            public int count { get; set; }
            public List<TopicData> items { get; set; }
        }

        #endregion
    }
}